﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.ComponentModel.DataAnnotations;
using System.Collections.Generic;

namespace ChartingModel
{
    public class AbstractViewModel<T> : INotifyPropertyChanged, IDataErrorInfo
    {
        public int errorCount { get; private set; }

        /// <summary>
        /// Event raised when a property has changed.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the PropertyChanged event.
        /// </summary>
        protected void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }


        /// <summary>
        /// called when labda expression used on viewmodel and the property changed
        /// </summary>
        /// <param name="property">the property.</param>
        public void OnPropertyChanged(Expression<Func<T, object>> property)
        {
            var propertyName = GetPropertyName(property);
            if (this.PropertyChanged != null)
            {
                var handler = PropertyChanged;
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        /// <summary>
        /// gets the name of the property
        /// </summary>
        /// <param name="expression">the expression</param>
        /// <returns></returns>
        private string GetPropertyName(Expression<Func<T, object>> expression)
        {
            if (expression == null)
            {
                throw new ArgumentNullException("propertyExpression");
            }

            var memberExpression = expression.Body as MemberExpression;
            if (memberExpression == null)
            {
                throw new ArgumentException("The expression is not a member access expresssion", "expression");
            }

            var property = memberExpression.Member as PropertyInfo;
            if (property == null)
            {
                throw new ArgumentException("The member access expression does not access a property", "expression");
            }

            var getMethod = property.GetGetMethod(true);
            if (getMethod.IsStatic)
            {
                throw new ArgumentException("The referenced property is a static property", "expression");
            }


            return memberExpression.Member.Name;
        }


        public virtual string this[string columnName]
        {
            get
            {
                PropertyInfo prop = GetType().GetProperty(columnName);
                IEnumerable<ValidationAttribute> validationMap = prop.GetCustomAttributes(typeof(ValidationAttribute), true)
                    .Cast<ValidationAttribute>();

                this.errorCount = 0;

                foreach (ValidationAttribute v in validationMap)
                {
                    try
                    {
                        v.Validate(prop.GetValue(this, null), columnName);
                    }
                    catch (ValidationException ex)
                    {
                        this.errorCount++;
                        return ex.Message;
                    }
                }
                return null;
            }
        }

        public void AddErrorToCount()
        {
            this.errorCount++;
        }

        string error;
        public string Error
        {
            get { return error; }
            set
            {
                error = value;
                OnPropertyChanged(vm => this.Error);
            }
        }

        public AbstractViewModel()
        {

        }

    }
}
